/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-06-18     82752       the first version
 */

#define DBG_TAG              "lora_as32"
#define DBG_LVL              DBG_INFO

#include <rtdbg.h>
#include "lora_as32.h"

struct lora_as32_set lora_module = {0};
rt_device_t com;
static char lora_rev_thread_stack[LORA_REV_THREAD_STACK_SIZE] = {0};

static void lora_as32_set_mode(lora_as32_set_t p_module, int mode)
{

    if (mode == p_module->cur_lora_mode) {
        return;
    }

    switch (mode) {
    case MODE_NORMAL:
        rt_pin_write(p_module->md0_pin_num, PIN_LOW);
        rt_pin_write(p_module->md1_pin_num, PIN_LOW);
        break;
    case MODE_WAKE:
        rt_pin_write(p_module->md0_pin_num, PIN_HIGH);
        rt_pin_write(p_module->md1_pin_num, PIN_LOW);
        break;
    case MODE_POWERLESS:
        rt_pin_write(p_module->md0_pin_num, PIN_LOW);
        rt_pin_write(p_module->md1_pin_num, PIN_HIGH);
        break;
    case MODE_SLEEP:
        rt_pin_write(p_module->md0_pin_num, PIN_HIGH);
        rt_pin_write(p_module->md1_pin_num, PIN_HIGH);
        break;
    default:
        break;
    }

    rt_thread_mdelay(1);
    while(!rt_pin_read(p_module->aux_pin_num));
    rt_thread_mdelay(2);

    p_module->cur_lora_mode = mode;
}

static void lora_as32_set_addr_and_channel(lora_as32_set_t p_module,
                                           uint16_t addr,
                                           uint8_t channel)
{

    uint8_t buf[8] = {0};
    uint32_t event_flag = 0;
    int try_set_addr_count = 5;
    rt_device_t serial = p_module->serial;
    uint8_t setting_args[6] = {0xC2, 0x00, 0x00, 0x1A, 0x10, 0xC4};


    rt_sem_take(&p_module->lora_doing_sem, RT_WAITING_FOREVER);

    lora_as32_set_mode(p_module, MODE_SLEEP);

    /* wait for the last lora data processing */
    rt_thread_mdelay(50);
    while(RT_EOK == rt_sem_trytake(&p_module->lora_uart_rev_sem)) {
        rt_device_read(p_module->serial, 0, (uint8_t *)buf, 1);
    }

    while (try_set_addr_count > 0) {

        LOG_D("the %d time to try setting lora addr\n", 6 - try_set_addr_count);

        setting_args[1] = (addr >> 8);
        setting_args[2] = (addr & 0xFF);
        setting_args[4] = channel;

        if (serial != RT_NULL) {
            rt_device_write(serial, 0, setting_args, 6);
        }

        if (rt_event_recv(&p_module->ack_event, (EVENT_OK_ACK | EVENT_ERROR_ACK),
                          RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
                          1000, &event_flag) == RT_EOK) {
            if (event_flag == EVENT_OK_ACK) {
                LOG_I("lora as32 set addr 0x%x, channel 0x%x successfully", addr, channel);
                break;
            } else if (event_flag == EVENT_ERROR_ACK) {
                LOG_E("lora as32 error ack\n");
            }
        }

        try_set_addr_count--;
    }

    if (try_set_addr_count <= 0) {
        LOG_E("lora as32 set addr failed\n");
    }

    lora_as32_set_mode(p_module, MODE_NORMAL);
    rt_sem_release(&p_module->lora_doing_sem);
}


static void lora_as32_reset(lora_as32_set_t p_module)
{
    uint8_t buf[8] = {0};
    rt_device_t serial = p_module->serial;
    uint8_t reset_cmd[3] = {0xC4, 0xC4, 0xC4};

    rt_sem_take(&p_module->lora_doing_sem, RT_WAITING_FOREVER);

    lora_as32_set_mode(p_module, MODE_SLEEP);

    /* wait for the last lora data processing */
    rt_thread_mdelay(50);
    while(RT_EOK == rt_sem_trytake(&p_module->lora_uart_rev_sem)) {
        rt_device_read(p_module->serial, 0, (uint8_t *)buf, 1);
    }

    rt_device_write(serial, 0, reset_cmd, 3);

    rt_thread_mdelay(10);
    while(!rt_pin_read(p_module->aux_pin_num));
    rt_thread_mdelay(2);

    lora_as32_set_mode(p_module, MODE_NORMAL);
    rt_sem_release(&p_module->lora_doing_sem);
}

static int lora_as32_get_arg(lora_as32_set_t p_module)
{
    int ret = 0;
    uint8_t buf[8] = {0};
    uint32_t event_flag = 0;
    rt_device_t serial = p_module->serial;
    uint8_t get_args_cmd[3] = {0xC1, 0xC1, 0xC1};

    rt_sem_take(&p_module->lora_doing_sem, RT_WAITING_FOREVER);

    lora_as32_set_mode(p_module, MODE_SLEEP);

    /* wait for the last lora data processing */
    rt_thread_mdelay(50);
    while(RT_EOK == rt_sem_trytake(&p_module->lora_uart_rev_sem)) {
        rt_device_read(p_module->serial, 0, (uint8_t *)buf, 1);
    }

    rt_device_write(serial, 0, get_args_cmd, 3);

    if (rt_event_recv(&p_module->ack_event, EVENT_ARG_ACK,
                      RT_EVENT_FLAG_CLEAR | RT_EVENT_FLAG_AND,
                      1000, &event_flag) == RT_EOK) {
        if (event_flag == EVENT_ARG_ACK) {
            ret = 0;
            LOG_I("lora as32 get args successfully\n");
        } else {
            ret = -1;
        }
    } else {
        ret = -1;
    }

    lora_as32_set_mode(p_module, MODE_NORMAL);
    rt_sem_release(&p_module->lora_doing_sem);
    return ret;
}

static void lora_rev_arg_handler(lora_as32_set_t p_module, uint8_t ch)
{
    uint8_t i = 3;

    static uint8_t hit_flag = 0;
    static uint8_t hit_ack_index = 0;
    static uint8_t hit_str_index = 0;

    /* here only chenk three kind of ack */
    const struct lora_as32_ack ack_list[3] = {
        {"OK", 2},        //when setting command successfully
        {"ERROR", 5},     //when setting command fail
        {{0xC0}, 6},      //the header of lora as32 module arguments
    };

    if (!hit_flag) {        /* first stage, find which ack is hit */

        while(i--) {
            if (ch == ack_list[i].ack[0]) {
                hit_flag = 1;
                hit_ack_index = i;
                hit_str_index = 1;
            }
        }
    } else {               /* second stage, process special acks */

        if (hit_ack_index == 0 ||
            hit_ack_index == 1) {

            if (ch == ack_list[hit_ack_index].ack[hit_str_index]) {
                hit_str_index++;

                if (hit_str_index >= ack_list[hit_ack_index].len) {

                    if (hit_ack_index == 0) {
                        rt_event_send(&p_module->ack_event, EVENT_OK_ACK);
                    } else {
                        rt_event_send(&p_module->ack_event, EVENT_ERROR_ACK);
                    }
                    hit_flag = 0;
                }
            } else {
                hit_flag = 0;
            }
        } else {            /* get the args */
            p_module->lora_arg[hit_str_index - 1] = ch;
            hit_str_index++;

            if (hit_str_index >= ack_list[hit_ack_index].len) {
                rt_event_send(&p_module->ack_event, EVENT_ARG_ACK);       //get arg finish
                hit_flag = 0;
            }
        }
    }
}

static rt_err_t lora_uart_callback(rt_device_t dev, rt_size_t size)
{
    //lora_as32_set_t p_module = (lora_as32_set_t) rt_container_of(dev,
    //                                                             struct lora_as32_set,
    //                                                             serial);
    lora_as32_set_t p_module = &lora_module;
    rt_sem_release(&p_module->lora_uart_rev_sem);

    return RT_EOK;
}

static uint8_t lora_uart_cal_fcs(uint8_t *msg_ptr, uint8_t len)
{
  uint8_t x;
  uint8_t sum;

  sum = 0;

  for (x = 0; x < len; x++, msg_ptr++)
    sum = sum + *msg_ptr;

  return sum;
}

static void lora_rev_process_thread(void *param)
{
    lora_as32_set_t p_module = (lora_as32_set_t)param;

    int             ret = 0;
    uint8_t         len = 0;
    uint8_t         ch = 0;

    uint8_t         cur_state = STATE_HEARDER1;
    uint8_t         msg_pos = 0;
    uint8_t         msg[LORA_MAX_REV] = {0};

    LOG_D("lora rev thread start\n");

    while (1) {
        rt_sem_take(&p_module->lora_uart_rev_sem, RT_WAITING_FOREVER);
        rt_device_read(p_module->serial, 0, (uint8_t *)&ch, 1);
        LOG_D("cur_state: %d, char: 0x%x\n", cur_state, ch);
        if (p_module->cur_lora_mode == MODE_NORMAL) {
            switch (cur_state) {

            case STATE_HEARDER1:
                if (LORA_UART_HEADER1 == ch) {
                    cur_state = STATE_HEARDER2;
                }

                break;

            case STATE_HEARDER2:
                if (LORA_UART_HEADER2 == ch) {
                    cur_state = STATE_LEN;
                } else {
                    cur_state = STATE_HEARDER1;
                }
                break;

            case STATE_LEN:
                len = ch;
                msg[POS_LEN] = len;
                if (len > LORA_MAX_REV) {
                    cur_state = STATE_HEARDER1;
                } else {
                    /* the len must at least contain the bytes of addrh, addrl and channel */
                    if (len > 3) {
                        cur_state = STATE_MSG;
                    } else {
                        cur_state = STATE_HEARDER1;
                    }
                }
                break;

            case STATE_MSG:

                msg[POS_MSG_START + msg_pos++] = ch;

                if (msg_pos >= len) {
                    cur_state = STATE_FCS;
                    msg_pos = 0;
                }
                break;

            case STATE_FCS:

                if (ch == lora_uart_cal_fcs(msg, len + 1)) {

                    ret = rt_mq_send(&p_module->rt_mq, msg, len + 1);
                    if (ret == -RT_EFULL) {

                    } else if (ret == RT_EOK) {

                    }
                } else {

                }
                cur_state = STATE_HEARDER1;
                break;

            default:
                break;
            }
        } else {
            lora_rev_arg_handler(p_module, ch);
        }

    }

}

static rt_err_t lora_as32_init(rt_device_t dev)
{
    int ret = 0;
    lora_as32_set_t p_module = (lora_as32_set_t) rt_container_of(dev,
                                                                 struct lora_as32_set,
                                                                 parent);
    if (p_module == NULL) {
        LOG_E("lora as32 init failed.(the struct is NULL)");
        return -1;
    }
    dev->user_data = (void *)p_module;

    /* GPIO init */
    p_module->aux_pin_num = rt_pin_get(AUX_PIN_NAME);
    p_module->md0_pin_num = rt_pin_get(MD0_PIN_NAME);
    p_module->md1_pin_num = rt_pin_get(MD1_PIN_NAME);

    rt_pin_mode(p_module->aux_pin_num, PIN_MODE_INPUT_PULLUP);
    rt_pin_mode(p_module->md0_pin_num, PIN_MODE_OUTPUT);
    rt_pin_mode(p_module->md1_pin_num, PIN_MODE_OUTPUT);

    p_module->cur_lora_mode = MODE_NORMAL;

    /* set in normal mode */
    lora_as32_set_mode(p_module, MODE_NORMAL);
    LOG_I("set lora as32 normal mode\n");

    /* get uart1 */
    p_module->serial = rt_device_find(LORA_AS32_UART_NAME);
    if (p_module->serial == RT_NULL) {
        LOG_E("can not find lora uart\n");
        return -1;
    }
    com = p_module->serial;
    LOG_I("find lora uart successfully\n");

    ret = rt_sem_init(&p_module->lora_uart_rev_sem,
                      "lora_uart_rev_sem",
                      0,
                      RT_IPC_FLAG_PRIO);
    if (ret != RT_EOK) {
        LOG_E("lora_uart_rev_sem init failed\n");
        return -1;
    }

    ret = rt_sem_init(&p_module->lora_doing_sem,
                      "lora_doing_sem",
                       1,
                       RT_IPC_FLAG_PRIO);
    if (ret != RT_EOK) {
        LOG_E("lora_doing_sem init failed\n");
        return -1;
    }

    /* create the message queue to transfer lora event */
    rt_mq_init(&p_module->rt_mq, "lora_mq",
               p_module->msg_pool,
               LORA_MAX_REV,
               sizeof(p_module->msg_pool),
               RT_IPC_FLAG_FIFO);

    ret = rt_event_init(&p_module->ack_event, "ack event", RT_IPC_FLAG_PRIO);
    if (ret != RT_EOK)
    {
        LOG_E("init event failed.\n");
        return -1;
    }

    /*lora_as32_set_mode(p_module, MODE_SLEEP);
    while(1) {
        rt_thread_mdelay(1000);
    }*/

    rt_thread_init(&p_module->lora_rev_thread,
                   "lora_rev_thread",
                   lora_rev_process_thread,
                   (void *)p_module,
                   &lora_rev_thread_stack[0],
                   sizeof(lora_rev_thread_stack),
                   2, 5);
    LOG_I("lora as32 init finish");
    return RT_EOK;
}

static rt_err_t lora_as32_open(rt_device_t dev,
                               rt_uint16_t oflag)
{
    int ret = 0;
    lora_as32_set_t p_module = (lora_as32_set_t)(dev->user_data);
    rt_device_t serial = p_module->serial;
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;

    if (p_module == NULL) {
        LOG_E("lora as32 init failed.(the struct is NULL)");
        return -1;
    }

    config.rx_bufsz = 256;
    config.tx_bufsz = 0;
    config.baud_rate = BAUD_RATE_9600;
    ret = rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);
    if (ret != RT_EOK) {
        LOG_E("config lora serial failed\n");
    }
    LOG_I("config lora serial successfully\n");

    ret = rt_device_open(serial, RT_SERIAL_RX_NON_BLOCKING | RT_SERIAL_TX_BLOCKING);
    if (ret != RT_EOK) {
        LOG_E("open lora serial failed\n");
    }
    LOG_I("open lora serial successfully\n");
    rt_device_set_rx_indicate(serial, lora_uart_callback);

    rt_thread_startup(&p_module->lora_rev_thread);

    return 0;
}

static rt_err_t lora_as32_close(rt_device_t dev)
{
    lora_as32_set_t p_module = (lora_as32_set_t)(dev->user_data);

    rt_device_close(p_module->serial);

    return 0;
}

static rt_size_t lora_as32_read(rt_device_t dev,
                                rt_off_t pos,
                                void *buffer,
                                rt_size_t size)
{
    lora_as32_set_t p_module = (lora_as32_set_t)(dev->user_data);
    uint8_t *buf = (uint8_t *)buffer;

    if (buf == NULL) {
        return -1;
    }

    if (rt_mq_recv(&p_module->rt_mq,
                   buf,
                   size,
                   RT_WAITING_FOREVER) == RT_EOK) {
        LOG_D("rev a msg: %x %x %x %x %x\n", buf[1], buf[2], buf[3], buf[4], buf[5]);
        return 0;
    } else {
        return -1;
    }

}

static rt_size_t lora_as32_write(rt_device_t dev,
                                 rt_off_t pos,
                                 const void *buffer,
                                 rt_size_t size)
{
    const uint8_t len_pos = 5;
    const uint8_t fcs_start_pos = len_pos;    // fcs start position is the positon of len.
    uint8_t *buf = (uint8_t *)buffer;
    lora_as32_set_t p_module = (lora_as32_set_t)(dev->user_data);
    rt_device_t serial = p_module->serial;

    uint8_t lora_buf[LORA_MAX_REV] = {0};

    if (size > (LORA_MAX_REV + 3)) {
        LOG_E("lora send msg size overflow\n");
        return -1;
    } else if (size < 5) {
        LOG_E("lora send msg at least contain lora addr, channel and cmd\n");
    }

    lora_buf[0] = buf[0];
    lora_buf[1] = buf[1];
    lora_buf[2] = buf[2];
    lora_buf[3] = LORA_UART_HEADER1;
    lora_buf[4] = LORA_UART_HEADER2;
    lora_buf[5] = size;
    lora_buf[6] = LORA_DEFAULT_ADDR >> 8;
    lora_buf[7] = LORA_DEFAULT_ADDR & 0xFF;
    lora_buf[8] = LORA_DEFAULT_CHANNEL;
    rt_memcpy(lora_buf + 9, buf + 3, size - 3);
    lora_buf[11 + size - 5] = lora_uart_cal_fcs(&lora_buf[fcs_start_pos],
                                               lora_buf[len_pos] + 1);

    rt_sem_take(&p_module->lora_doing_sem, RT_WAITING_FOREVER);
    rt_device_write(serial, 0, lora_buf, size + 7);
    rt_sem_release(&p_module->lora_doing_sem);
    return 0;
}

static rt_err_t lora_as32_control(rt_device_t dev,
                                  int cmd,
                                  void *args)
{
    lora_as32_set_t p_module = (lora_as32_set_t)(dev->user_data);
    uint8_t *buf = (uint8_t *)args;

    if (p_module == NULL) {
        return -1;
    }

    switch (cmd) {

    case SET_MODE:
        lora_as32_set_mode(p_module, buf[0]);
        break;

    case SET_ADDR:
        lora_as32_set_addr_and_channel(p_module, (buf[0] << 8) | buf[1], buf[2]);
        break;

    case RESET:
        lora_as32_reset(p_module);
        break;

    case GET_ARGS:

        if(!lora_as32_get_arg(p_module)) {
            LOG_D("lora args is %x %x %x %x %x %x\n",
                    p_module->lora_arg[0],
                    p_module->lora_arg[1],
                    p_module->lora_arg[2],
                    p_module->lora_arg[3],
                    p_module->lora_arg[4],
                    p_module->lora_arg[5]);
            rt_memcpy(buf, p_module->lora_arg, 6);
        }
        break;
    default:
        break;
    }

    return 0;
}

#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops lora_as32_ops =
{
    lora_as32_init,
    lora_as32_open,
    lora_as32_close,
    lora_as32_read,
    lora_as32_write,
    lora_as32_control
};
#endif

/*
 * lora as32 module register
 */
int hw_lora_as32_register(const char *name)
{
    int result;
    rt_device_t device;

    lora_as32_set_t p_module = &lora_module;

    device = &p_module->parent;

#ifdef RT_USING_DEVICE_OPS
    device->ops         = &lora_as32_ops;
#else
    device->init        = lora_as32_init;
    device->open        = lora_as32_open;
    device->close       = lora_as32_close;
    device->read        = lora_as32_read;
    device->write       = lora_as32_write;
    device->control     = lora_as32_control;
#endif
    device->type        = RT_Device_Class_Char;
    device->rx_indicate = RT_NULL;
    device->tx_complete = RT_NULL;

    result = rt_device_register(device, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
    if (result != RT_EOK)
    {
        LOG_E("lora_as32 module[%s] register err code: %d", name, result);
        return result;
    }

    LOG_I("rt_sensor[%s] init success", name);
    return RT_EOK;
}

int lora_as32_register(void)
{
    return hw_lora_as32_register("lora_as32");
}
INIT_DEVICE_EXPORT(lora_as32_register);
